package cn.ygh.bbo.common.utils;

import cn.ygh.bbo.common.annotation.Excel;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author ygh
 * @Desc 通用excel处理工具
 * @date 2019/11/3
 */
public class ExcelUtils {
    private static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";
    private static final String YMD = "yyyy-MM-dd";

    /**
     * 格式化对象
     */
    private static SimpleDateFormat sdf = new SimpleDateFormat(YMDHMS);

    /**
     * 插入数据
     *
     * @param row   列号
     * @param col   行号
     * @param style 样式
     * @param value 值
     */
    public static void createCell(Row row, Integer col, CellStyle style, Object value) {
        Cell cell = row.createCell(col);
        if (style != null) {
            cell.setCellStyle(style);
        }
        if (value != null) {
            cell.setCellValue(value.toString());
        }
    }

    /**
     * 从文本格中获取文本，去掉前后空格，前后回车
     *
     * @param cell
     * @return
     */
    public static String stringCellValue(Cell cell) {
        if (cell == null) {
            return null;
        }
        int type = cell.getCellType();
        String result = "";
        switch (type) {
            case Cell.CELL_TYPE_STRING:
                result = cell.getStringCellValue();
                break;
            case Cell.CELL_TYPE_BOOLEAN:
                result = String.valueOf(cell.getBooleanCellValue());
                break;
            case Cell.CELL_TYPE_NUMERIC:
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    result = sdf.format(cell.getDateCellValue());
                } else {
                    result = String.valueOf(cell.getNumericCellValue());
                }
                break;
            case Cell.CELL_TYPE_FORMULA:
                result = String.valueOf(cell.getCellFormula()); break;
            default:
                result = cell.toString(); break;
        }
        return result.trim();
    }


    /**
     * 根据关键字从excel的row中获取整数
     *
     * @param row0 第一行
     * @param row  当前行
     * @param key  关键字
     * @return
     */
    public static Integer integerValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1){
            return null;
        }
        Cell cell = row.getCell(i);
        String s = stringCellValue(cell);
        if (StringUtils.isBlank(s)) {
            return null;
        }
        return new Double(s).intValue();
    }

    /**
     * 根据关键字从excel的row中获取整数
     *
     * @param row0 第一行
     * @param row  当前行
     * @param key  关键字
     * @return
     */
    public static BigDecimal decimalValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1){
            return null;
        }
        Cell cell = row.getCell(i);
        String s = stringCellValue(cell);
        if (StringUtils.isBlank(s)) {
            return null;
        }
        return new BigDecimal(s);
    }

    /**
     * 根据关键字从excel的row中获取长整数
     *
     * @param row0 第一行
     * @param row  当前行
     * @param key  关键字
     * @return
     */
    public static Long longValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1) {
            return null;
        }
        Cell cell = row.getCell(i);
        String s = stringCellValue(cell);
        if (StringUtils.isBlank(s)) {
            return null;
        }
        return new Double(s).longValue();
    }

    /**
     * 根据关键字从excel的row中获取小数
     *
     * @param row0 第一行
     * @param row  当前行
     * @param key  关键字
     * @return
     */
    public static Double doubleValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1) {
            return null;
        }
        Cell cell = row.getCell(i);
        String s = stringCellValue(cell);
        if (StringUtils.isBlank(s)) {
            return null;
        }
        return new Double(s);
    }

    /**
     * 根据关键字从excel的row中获取文本
     *
     * @param row0 第一行
     * @param row  当前行
     * @param key  关键字
     * @return
     */
    public static String stringValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1) {
            return null;
        }
        Cell cell = row.getCell(i);
        return stringCellValue(cell);
    }

    /**
     * 获取时间类型
     * @param row0
     * @param row
     * @param key
     * @return
     */
    public static Date dateValueOfRow(Row row0, Row row, String key) {
        short i = indexCol(row0, key);
        if (i == -1) {
            return null;
        }
        Cell cell = row.getCell(i);
        String s = stringCellValue(cell);
        if (StringUtils.isBlank(s)) {
            return null;
        }
        try {
            if (s.length() == 10) {
                return new SimpleDateFormat(YMD).parse(s);
            } else {
                return new SimpleDateFormat(YMDHMS).parse(s);
            }
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 根据关键字从excel的row中获取列序号
     *
     * @param row HSSFRow
     * @param key 第一行的关键字
     * @return
     */
    public static short indexCol(Row row, String key) {
        //得到行数
        for (short i = 0; i < row.getPhysicalNumberOfCells(); i++) {
            Cell cell = row.getCell(i);
            String cs = (cell == null) ? "" : cell.getStringCellValue();
            if (key.equals(cs)) {
                return i;
            }
        }
        return -1;
    }

    /**
     *获取类属性值
     * @param row0
     * @param row
     * @param field
     * @return
     */
    public static Object getValue(Row row0,Row row,Field field){
        Class<?> type = field.getType();
        if (type.equals(Integer.class)){
            return integerValueOfRow(row0, row, field.getName());
        }
        if (type.equals(BigDecimal.class)){
            return decimalValueOfRow(row0, row, field.getName());
        }
        if (type.equals(Double.class)){
            return doubleValueOfRow(row0, row, field.getName());
        }
        if (type.equals(Date.class)){
            return dateValueOfRow(row0, row, field.getName());
        }
        if (type.equals(String.class)){
            return stringValueOfRow(row0, row, field.getName());
        }
        if (type.equals(Long.class)){
            return longValueOfRow(row0, row, field.getName());
        }
        return null;
    }

    /**
     * 获取所有有excel注解的字段
     * @param tClass
     * @return
     */
    public static List<Field> getFieldsFromExcel(Class tClass){
        List<Field>fieldList=new ArrayList<>();
        Field[] fields = tClass.getDeclaredFields();
        for (Field field:fields){
            if (field.getAnnotation(Excel.class)!=null){
                field.setAccessible(true);
                fieldList.add(field);
            }
        }
        return fieldList;
    }

    /**
     * 文件转成List对象
     * @param inputStream
     * @param tClass
     * @param <T>
     * @return
     * @throws IOException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws InvalidFormatException
     */
    public static <T>List<T> streamToList(InputStream inputStream,Class<T>tClass) throws IOException, IllegalAccessException, InstantiationException, InvalidFormatException {
        //获取所有的excel字段
        List<Field>fieldList=getFieldsFromExcel(tClass);
        //excel解析
        Workbook workbook=WorkbookFactory.create(inputStream);
        //得到第一个sheet
        Sheet sheet = workbook.getSheetAt(0);
        //第0行英文，第1行中文，第2行数据
        Row row0 = sheet.getRow(0);
        //行数
        int rowNo = sheet.getPhysicalNumberOfRows();
        List<T>tList=new ArrayList<>();
        for (int i = 2; i < rowNo; i++) {
            Row row = sheet.getRow(i);
            if(row.getCell(0)==null){
                break;
            }
            T t=tClass.newInstance();
            for (Field field:fieldList){
                Object o=getValue(row0, row, field);
                if (o!=null){
                    field.set(t, o);
                }

            }
            tList.add(t);
        }
        return tList;
    }

    /**
     * list转成文件
     * @param list
     * @param tClass
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    public static <T>Workbook listToWorkbook(List<T> list,Class<T>tClass) throws IllegalAccessException {
        List<Field> fieldList=ExcelUtils.getFieldsFromExcel(tClass);
        HSSFWorkbook wb = new HSSFWorkbook();
        // 第二步，在webbook中添加一个sheet,对应Excel文件中的sheet
        HSSFSheet sheet = wb.createSheet("数据");
        // 第三步，在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short
        // 第四步，创建单元格，并设置值表头 设置表头居中
        HSSFCellStyle style = wb.createCellStyle();
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 创建一个居中格式
        int nowrow=0;
        //excel表格第一行，插入的是字段英文名。
        HSSFRow row = sheet.createRow(nowrow++);
        for (int i = 0; i < fieldList.size(); i++) {
            ExcelUtils.createCell(row, i, style, fieldList.get(i).getName());
        }
        //excel表格第二行，插入的是中文名
        row = sheet.createRow(nowrow++);
        for (int i = 0; i < fieldList.size(); i++) {
            Excel excel=fieldList.get(i).getAnnotation(Excel.class);
            ExcelUtils.createCell(row, i, style, excel.value());
        }
        //开始插入数据
        for (T val : list) {
            row = sheet.createRow(nowrow++);
            int nowcol=0;
            for (Field field:fieldList){
                Object o=field.get(val);
                if (o instanceof Date){
                    ExcelUtils.createCell(row, nowcol++, style
                            , new SimpleDateFormat(YMDHMS).format(o));
                }else {
                    ExcelUtils.createCell(row, nowcol++, style, o==null?"":o.toString());
                }

            }
        }
        return wb;
    }
}
